﻿/* 
 * Copyright (c) 2008 Intel Corporation
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * -- Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * -- Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * -- Neither the name of the Intel Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE INTEL OR ITS
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ExtensionLoader;
using HttpServer;
using WebDAVSharp;
using OpenMetaverse;
using OpenMetaverse.Http;
using CableBeachMessages;

using Utils = OpenMetaverse.Utils;
using System.Web;
using System.Net;

namespace InventoryServer.Extensions
{
    public class WebDAVFrontend : IExtension<InventoryServer>
    {
        protected InventoryServer server;
        WebDAVListener webdav;
        protected Dictionary<string, WebDAVLockResponse> lockedResources = new Dictionary<string, WebDAVLockResponse>();

        /// <summary>
        /// Describes where starts users root in path.
        /// 
        /// For example in path: /
        ///  this would be 0
        /// and in path: /inventory/user_uuid/
        ///  this would be 2
        /// </summary>
        protected int rootNumInPath = 0;

        public AuthenticationType AuthenticationType
        {
            get { return webdav.Authentication; }
            set { webdav.Authentication = value; }
        }

        public WebDAVFrontend()
        {
        }

        public virtual bool Start(InventoryServer server)
        {
            this.server = server;
            server.ServiceRegistrationProvider.RegisterService(new Uri(CableBeachServices.FILESYSTEM_WEBDAV), CreateCapabilitiesHandler);

            webdav = new WebDAVListener(server.HttpServer, @"^/inventory");
            rootNumInPath = 1;
            webdav.Authentication = AuthenticationType.Digest;
            webdav.OnPropFind += PropFindHandler;
            webdav.OnLock += LockHandler;
            webdav.OnUnlock += UnlockHandler;
            webdav.OnNewCol += NewColHandler;
            webdav.OnMove += MoveHandler;
            webdav.OnGet += GetHandler;
            webdav.OnPut += PutHandler;
            webdav.OnDelete += DeleteHandler;
            webdav.OnCopy += CopyHandler;
            webdav.OnPropPatch += PropPatchHandler;
            webdav.OnDigestAuthenticate += OnDigestAuthenticateHandler;
            return true;
        }

        void CreateCapabilitiesHandler(Uri identity, ref Dictionary<Uri, Uri> capabilities)
        {
            Uri[] caps = new Uri[capabilities.Count];
            capabilities.Keys.CopyTo(caps, 0);
            bool capReturned = false;

            for (int i = 0; i < caps.Length; i++)
            {
                Uri cap = caps[i];
                string capName = cap.ToString();

                switch (capName)
                {
                    case CableBeachServices.FILESYSTEM_GET_WEBDAV_ROOT:
                        string uri = server.HttpUri.ToString();
                        if (!uri.EndsWith("/"))
                            uri += "/";
                        uri += "inventory/";
                        capabilities[cap] = new Uri(uri);
                        capReturned = true;
                        break;
                    default:
                        break;
                }
            }
        }

        bool OnDigestAuthenticateHandler(string username, out PasswordFormat format, out string password)
        {
            Uri owner;

            format = PasswordFormat.Plain;
            password = String.Empty; //TODO: this should eventually be some session generated passwd

            if (Uri.TryCreate(username, UriKind.Absolute, out owner))
            {
                UUID agentID = server.FilesystemProvider.IdentityToUUID(owner);
                if (server.FilesystemProvider.GetDefaultParent(owner, agentID, "application/vnd.ll.folder") != UUID.Zero)
                    return true;
            }

            return false;
        }

        protected HttpStatusCode PropPatchHandler(string username, Uri uri, string uriPath, string nspace, Dictionary<string, string> setProperties,
            List<string> removeProperties, out Dictionary<string, HttpStatusCode> multiStatus)
        {
            //NOTE: The method is atomic: All property changes defined in the request are made, or none is made.

            multiStatus = null;
            Uri owner;
            if (Uri.TryCreate(username, UriKind.Absolute, out owner))
            {
                UUID agentID = server.FilesystemProvider.IdentityToUUID(owner);

                InventoryFolder inventory;
                if (server.FilesystemProvider.TryFetchRootFolder(owner, agentID, out inventory) == BackendResponse.Success)
                {
                    List<IWebDAVResource> davEntries = new List<IWebDAVResource>();
                    string searchPath = HttpUtility.UrlDecode(PathToSearchPath(uriPath)); //convert %20 in to space etc.

                    InventoryBase invObject = WebDAVHelper.PathToInventory(searchPath, inventory);
                    if (invObject == null) //Couldn't find the path requested.
                    {
                        return HttpStatusCode.NotFound;
                    }

                    //instead of just trying to convert inventory item to WebDAV resource, first try to fetch it from WebDAVPropertyProvider
                    IWebDAVResource resource = server.WebDAVPropertyProvider.Load(uriPath);
                    if (resource == null) //didn't find, now try converting from inventory object
                    {
                        resource = WebDAVHelper.InventoryToDAV(uriPath, invObject);
                    }

                    //now set and/or remove properties
                    //hardcoded properties that can't be removed_
                    //  creationdate, displayname, getcontentlanguage, getcontentlength, getcontenttype, getlastmodified, resourcetype

                    multiStatus = new Dictionary<string, HttpStatusCode>();
                    if (CheckRequiredProperties(removeProperties, ref multiStatus)) // failed dependency
                    {
                        foreach (string s in setProperties.Keys)
                        {
                            if (!multiStatus.ContainsKey(s))
                                multiStatus.Add(s, (HttpStatusCode)424); //HTTP/1.1 424 Failed Dependency
                        }

                        foreach (string s in removeProperties)
                        {
                            if (!multiStatus.ContainsKey(s))
                                multiStatus.Add(s, (HttpStatusCode)424); //HTTP/1.1 424 Failed Dependency
                        }
                        return (HttpStatusCode)207;
                    }
                    else
                    {
                        if (nspace == "DAV:")
                        {
                            foreach(KeyValuePair<string, string> property in setProperties)
                            {
                                if (IsStandardProperty(property.Key))
                                {
                                    HttpStatusCode status = SetStandardProperty(ref resource, property.Key, property.Value);
                                    multiStatus.Add(property.Key, status);
                                }
                                else
                                {
                                    SetCustomProperty(nspace, property.Key, property.Value, ref multiStatus, ref resource);
                                }
                            }

                            foreach (string property in removeProperties)
                            {
                                RemoveCustomProperty(nspace, property, ref multiStatus, ref resource);
                            }
                        }
                        else
                        {
                            foreach (KeyValuePair<string, string> property in setProperties)
                            {
                                SetCustomProperty(nspace, property.Key, property.Value, ref multiStatus, ref resource);
                            }

                            foreach (string property in removeProperties)
                            {
                                RemoveCustomProperty(nspace, property, ref multiStatus, ref resource);
                            }
                        }

                        server.WebDAVPropertyProvider.Save(resource);
                        #region old code for reference
                        ////now the IWebDAVResource should be converted back to appropriate InventoryBase
                        //invObject = WebDAVHelper.DAVToInventory(invObject, resource);
                        //if (invObject is InventoryItem)
                        //    server.FilesystemProvider.TryCreateItem(owner, agentID, (InventoryItem)invObject);
                        //else if (invObject is InventoryFolder)
                        //    server.FilesystemProvider.TryCreateFolder(owner, agentID, (InventoryFolder)invObject);
                        #endregion

                        if (multiStatus.Count >= 1)
                            return (HttpStatusCode)207;
                    }
                }
            }
            return HttpStatusCode.Forbidden;
        }

        private void RemoveCustomProperty(string nspace, string name, ref Dictionary<string, HttpStatusCode> multiStatus, ref IWebDAVResource resource)
        {
            WebDAVProperty foundProp = null;
            foreach (WebDAVProperty prop in resource.CustomProperties)
            {
                if (prop.Namespace == nspace && prop.Name == name)
                {
                    foundProp = prop;
                    break;
                }
            }
            if (foundProp != null)
                resource.CustomProperties.Remove(foundProp);
            multiStatus.Add(name, HttpStatusCode.OK);
        }

        private void SetCustomProperty(string nspace, string name, string value, ref Dictionary<string, HttpStatusCode> multiStatus,
            ref IWebDAVResource resource)
        {
            WebDAVProperty foundProp = null;
            foreach (WebDAVProperty prop in resource.CustomProperties)
            {
                if (prop.Namespace == nspace && prop.Name == name)
                {
                    foundProp = prop;
                    break;
                }
            }
            if (foundProp != null)
                foundProp.Value = value;
            else
            {
                resource.CustomProperties.Add(new WebDAVProperty(name, nspace, value));
            }
            multiStatus.Add(name, HttpStatusCode.OK);
        }

        private HttpStatusCode SetStandardProperty(ref IWebDAVResource resource, string name, string value)
        {
            HttpStatusCode ret = HttpStatusCode.OK;
            switch (name)
            {
                case "creationdate":
                    DateTime c_date = resource.CreationDate;
                    if (!DateTime.TryParse(value, out c_date))
                        ret = HttpStatusCode.Conflict;
                    else
                        resource.CreationDate = c_date;
                    break;

                case "displayname":
                case "getcontentlanguage":
                    return HttpStatusCode.InternalServerError; //TODO: implement

                case "getcontentlength":
                    int c_length = 0;
                    if (!Int32.TryParse(value, out c_length))
                        ret = HttpStatusCode.Conflict;
                    else
                        resource.ContentLength = c_length;
                    break;

                case "getcontenttype":
                    resource.ContentType = value;
                    break;

                case "getlastmodified":
                    DateTime lm_date = resource.LastModifiedDate;
                    if (!DateTime.TryParse(value, out lm_date))
                        ret = HttpStatusCode.Conflict;
                    else
                        resource.LastModifiedDate = lm_date;
                    break;

                case "resourcetype":
                    return HttpStatusCode.InternalServerError; //TODO: implement
                default:
                    return HttpStatusCode.InternalServerError;
            }
            return ret;
        }

        private bool IsStandardProperty(string name)
        {
            switch (name)
            {
                case "creationdate":
                case "displayname":
                case "getcontentlanguage":
                case "getcontentlength":
                case "getcontenttype":
                case "getlastmodified":
                case "resourcetype":
                    return true;
                default:
                    return false;
            }
        }

        bool CheckRequiredProperties(List<string> removeProperties, ref Dictionary<string, HttpStatusCode> multiStatus)
        {
            bool failedDependency = false;
            if (removeProperties.Contains("creationdate"))
            {
                multiStatus.Add("creationdate", HttpStatusCode.Forbidden);
                failedDependency = true;
            }
            if (removeProperties.Contains("displayname"))
            {
                multiStatus.Add("displayname", HttpStatusCode.Forbidden);
                failedDependency = true;
            }
            if (removeProperties.Contains("getcontentlanguage"))
            {
                multiStatus.Add("getcontentlanguage", HttpStatusCode.Forbidden);
                failedDependency = true;
            }
            if (removeProperties.Contains("getcontentlength"))
            {
                multiStatus.Add("getcontentlength", HttpStatusCode.Forbidden);
                failedDependency = true;
            }
            if (removeProperties.Contains("getcontenttype"))
            {
                multiStatus.Add("getcontenttype", HttpStatusCode.Forbidden);
                failedDependency = true;
            }
            if (removeProperties.Contains("getlastmodified"))
            {
                multiStatus.Add("getlastmodified", HttpStatusCode.Forbidden);
                failedDependency = true;
            }
            if (removeProperties.Contains("resourcetype"))
            {
                multiStatus.Add("resourcetype", HttpStatusCode.Forbidden);
                failedDependency = true;
            }
            return failedDependency;
        }

        HttpStatusCode CopyHandler(string username, Uri source, string destination, DepthHeader depth, bool overwrite, string[] ifHeaders, out Dictionary<string, HttpStatusCode> multiStatusValues)
        {
            multiStatusValues = null;

            //first thing to do is to check source and destination
            string[] srcParts = source.ToString().Split('/');
            string[] dstParts = destination.Split('/');
            for (int i = 0; i < 3; i++)
            {
                if (srcParts[i] != dstParts[i])
                {
                    //error, some of the following has happened
                    //a) source or destination did not contain the whole absolute uri
                    //b) source and destination use different protocol http vs. https
                    //c) source and destination are in different domain
                    Console.WriteLine("Error occurred while comparing source and destination uris. Source part {0}, Destination part {1}",
                        srcParts[i], dstParts[i]);
                    return HttpStatusCode.BadGateway;
                }
            }

            return CopyWorker(username, source, destination, 3 + rootNumInPath, depth, overwrite, ifHeaders, out multiStatusValues);
        }

        /// <summary>
        /// Copies source resource to destination
        /// </summary>
        /// <param name="username">The username.</param>
        /// <param name="source">The source.</param>
        /// <param name="destination">The destination.</param>
        /// <param name="rootPos">The position of root resource</param>
        /// <param name="depth">Depth header value</param>
        /// <param name="overwrite">if set to <c>true</c> [overwrite].</param>
        /// <param name="ifHeaders">If headers.</param>
        /// <param name="multiStatusValues">The multi status values to return</param>
        /// <returns></returns>
        protected HttpStatusCode CopyWorker(string username, Uri source, string destination, int rootPos, DepthHeader depth,
            bool overwrite, string[] ifHeaders, out Dictionary<string, HttpStatusCode> multiStatusValues)
        {
            multiStatusValues = null;
            string[] srcParts = source.ToString().Split('/');
            srcParts = DecodeUri(srcParts);
            string[] dstParts = destination.ToString().Split('/');
            dstParts = DecodeUri(dstParts);

            //now look up the source and destination from the inventory; 3rd field is the first resource element in array
            Uri owner;
            if (Uri.TryCreate(username, UriKind.Absolute, out owner))
            {
                UUID agentID = server.FilesystemProvider.IdentityToUUID(owner);

                InventoryFolder inventory;
                if (server.FilesystemProvider.TryFetchRootFolder(owner, agentID, out inventory) == BackendResponse.Success)
                {
                    //find the source
                    InventoryBase foundSource = null;
                    if (srcParts.Length == rootPos)
                        return HttpStatusCode.Forbidden;
                    if (srcParts.Length >= rootPos + 1)
                    {
                        string[] sourceSearchPath = new string[srcParts.Length - rootPos];
                        Array.Copy(srcParts, rootPos, sourceSearchPath, 0, sourceSearchPath.Length);
                        if (!FindInventoryItem(inventory.Children, sourceSearchPath, out foundSource))
                            return HttpStatusCode.NotFound; //Source resource not found                     
                    }

                    //find the destination and its parent
                    InventoryBase foundDestination = null;
                    InventoryFolder foundDestinationParent = null;
                    if (dstParts.Length == rootPos)
                        return HttpStatusCode.Forbidden;
                    if (dstParts.Length >= rootPos + 1)
                    {
                        string[] dstSearchPath = new string[dstParts.Length - rootPos -1]; //strip everything before root resource and last resource
                        Array.Copy(dstParts, rootPos, dstSearchPath, 0, dstSearchPath.Length);
                        if (!FindInventoryItem(inventory.Children, dstSearchPath, out foundDestination))
                            return HttpStatusCode.NotFound;
                        else
                        {
                            if (dstSearchPath.Length == 0)
                                foundDestination = (InventoryBase)inventory;

                            if (foundDestination is InventoryFolder)
                            {
                                foundDestinationParent = (InventoryFolder)foundDestination;
                                foundDestination = null;
                                foreach (InventoryBase item in foundDestinationParent.Children.Values)
                                {
                                    if (item.Name == dstParts[dstParts.Length - 1])
                                    {
                                        foundDestination = item;
                                        break;
                                    }
                                }
                            }
                            else
                                return HttpStatusCode.Conflict;
                        }
                    }

                    //now copy
                    multiStatusValues = new Dictionary<string, HttpStatusCode>();
                    bool status = CopyResource(owner, agentID, destination, foundSource, foundDestinationParent, foundDestination, overwrite, ref multiStatusValues, dstParts[dstParts.Length-1]);
                    if (multiStatusValues.Count == 1 && multiStatusValues.ContainsKey(destination.ToString()))
                    {
                        HttpStatusCode ret = multiStatusValues[destination.ToString()];
                        multiStatusValues = null;
                        return ret;
                    }
                    else if (multiStatusValues.Count > 0)
                        return (HttpStatusCode)207;
                    else
                    {
                        multiStatusValues = null;
                        if (foundDestination == null)
                            return HttpStatusCode.Created;
                        else
                            return HttpStatusCode.NoContent;
                    }
                }
            }

            return HttpStatusCode.InternalServerError;
        }

        protected bool CopyResource(Uri owner, UUID agentID, string destinationURI, InventoryBase source, InventoryFolder destinationParent,
            InventoryBase destination, bool overwrite, ref Dictionary<string, HttpStatusCode> multiStatusValues, string destinationName)
        {
            if (destination == null)//no previous entry. just copy
            {
                if (source is InventoryItem)
                {
                    InventoryItem item = new InventoryItem((InventoryItem)source);
                    item.ID = UUID.Random();
                    item.ParentID = destinationParent.ID;
                    item.Name = destinationName;
                    BackendResponse result = server.FilesystemProvider.TryCreateItem(owner, agentID, item);
                    if (result != BackendResponse.Success)
                    {
                        return false;
                    }
                }
                else if (source is InventoryFolder)
                {
                    //first save folder, then process sub resources
                    InventoryFolder oldFolder = (InventoryFolder)source;
                    InventoryFolder folder = new InventoryFolder(oldFolder);
                    folder.ID = UUID.Random();
                    folder.ParentID = destinationParent.ID;
                    folder.Name = destinationName;
                    folder.Children = new Dictionary<UUID, InventoryBase>(); //remove old references from folder
                    BackendResponse result = server.FilesystemProvider.TryCreateFolder(owner, agentID, folder);
                    if (result != BackendResponse.Success)
                    {
                        return false;
                    }
                    else
                    {
                        foreach (InventoryBase item in oldFolder.Children.Values)
                        {
                            item.ParentID = folder.ID;
                            string subDstURI = destinationURI;
                            if (!subDstURI.EndsWith("/"))
                                subDstURI += "/";
                            subDstURI += HttpUtility.UrlPathEncode(item.Name);
                            if (!CopyResource(owner, agentID, subDstURI, item, folder, null, overwrite, ref multiStatusValues, item.Name))
                            {
                                //something bad happened
                                multiStatusValues.Add(subDstURI, HttpStatusCode.PreconditionFailed);
                            }
                        }
                    }
                }
                else
                {
                    return false;
                }
            }
            else if (destination != null && overwrite == true) //check if item exists, delete and add new
            {
                if (source is InventoryItem)
                {
                    InventoryItem item = new InventoryItem((InventoryItem)source);
                    item.ParentID = destinationParent.ID;
                    if (destination is InventoryFolder)
                    {
                        DeleteResource(destinationURI, destination, owner, agentID, ref multiStatusValues);
                        item.ID = UUID.Random();
                    }
                    else
                        item.ID = destination.ID; //updates file to new version
                    
                    
                    BackendResponse result = server.FilesystemProvider.TryCreateItem(owner, agentID, item);
                    if (result != BackendResponse.Success)
                        return false;
                }
                else if (source is InventoryFolder)
                {
                    InventoryFolder oldFolder = (InventoryFolder)source;
                    InventoryFolder folder = new InventoryFolder(oldFolder);
                    folder.ParentID = destinationParent.ID;
                    folder.Children = new Dictionary<UUID, InventoryBase>();
                    InventoryFolder desinationFolder = null;

                    BackendResponse folderSaveResult = BackendResponse.Success;
                    if (destination is InventoryItem)
                    {
                        DeleteResource(destinationURI, destination, owner, agentID, ref multiStatusValues);
                        folder.ID = UUID.Random();

                        //save folder
                        folderSaveResult = server.FilesystemProvider.TryCreateFolder(owner, agentID, folder);
                        desinationFolder = folder;
                    }
                    else if (destination is InventoryFolder)
                        desinationFolder = (InventoryFolder)destination;
                    else
                        return false;

                    if (folderSaveResult != BackendResponse.Success)
                    {
                        return false;
                    }
                    else
                    {
                        foreach (InventoryBase item in oldFolder.Children.Values)
                        {
                            InventoryBase subDst = null;
                            foreach (InventoryBase subitem in desinationFolder.Children.Values)
                            {
                                if (subitem.Name == item.Name)
                                {
                                    subDst = subitem;
                                    break;
                                }
                            }
                            item.ParentID = folder.ID;
                            string subDstURI = destinationURI;
                            if (!subDstURI.EndsWith("/"))
                                subDstURI += "/";
                            subDstURI += HttpUtility.UrlPathEncode(item.Name);
                            if (!CopyResource(owner, agentID, subDstURI, item, desinationFolder, subDst, overwrite, ref multiStatusValues, item.Name))
                            {
                                //something bad happened
                                multiStatusValues.Add(subDstURI, HttpStatusCode.PreconditionFailed);
                            }
                        }
                    }
                }
                else
                {
                    return false;
                }
            }
            else
            {
                //can't do shit. preconditions failed
                multiStatusValues.Add(destinationURI, HttpStatusCode.PreconditionFailed);
                return false;
            }
            return true;
        }

        protected HttpStatusCode DeleteHandler(Uri uri, string username, out Dictionary<string, HttpStatusCode> multiStatus)
        {
            multiStatus = null;
            string path = uri.ToString();
            // If last element is folder, we need to remove the last '/' from the string
            // otherwise FindInventoryItem() will get a url parts list that has last item empty '' due to '/' split!
            if (path[path.Length-1] == '/')
                path = path.Substring(0, path.Length-1);
            string[] parts = path.Split('/');
            parts = DecodeUri(parts);

            Uri owner;
            if (Uri.TryCreate(username, UriKind.Absolute, out owner))
            {
                UUID agentID = server.FilesystemProvider.IdentityToUUID(owner);

                InventoryFolder inventory;
                if (server.FilesystemProvider.TryFetchRootFolder(owner, agentID, out inventory) == BackendResponse.Success)
                {
                    // Find the resource to delete
                    InventoryBase foundResource = null;
                    if (parts.Length == 3 + rootNumInPath)
                        return HttpStatusCode.Forbidden;
                    if (parts.Length >= 4 + rootNumInPath)
                    {
                        string[] uriSearchPath = new string[parts.Length - 3 - rootNumInPath];
                        Array.Copy(parts, 3 + rootNumInPath, uriSearchPath, 0, uriSearchPath.Length);
                        if (!FindInventoryItem(inventory.Children, uriSearchPath, out foundResource))
                            return HttpStatusCode.NotFound; //Item to delete not found                        
                    }

                    // Now delete the resource
                    multiStatus = new Dictionary<string, HttpStatusCode>();
                    DeleteResource(uri.ToString(), foundResource, owner, agentID, ref multiStatus);
                    if (multiStatus.Count == 1 && multiStatus.ContainsKey(uri.ToString()))
                    {
                        HttpStatusCode ret = multiStatus[uri.ToString()];
                        multiStatus = null;
                        return ret;
                    }
                    else if (multiStatus.Count > 0)
                        return (HttpStatusCode)207;
                    else
                    {
                        multiStatus = null;
                        return HttpStatusCode.NoContent;
                    }
                }
            }

            return HttpStatusCode.InternalServerError;
        }

        /// <summary>
        /// Deletes the resource and possible subresources.
        /// </summary>
        /// <param name="uri">The URI of resource</param>
        /// <param name="resource">InventoryBase representation of the resource.</param>
        /// <param name="owner">The owner.</param>
        /// <param name="agentID">The agent ID.</param>
        /// <param name="multistatus">Status codes of deleting</param>
        protected void DeleteResource(string uri, InventoryBase resource, Uri owner, UUID agentID, ref Dictionary<string, HttpStatusCode> multistatus)
        {
            // First delete recursively possible sub resources
            // NOTE: cannot delete recursively because dictionary size will change and crash the "parent" foreach loop
            //       so i changed this a bit to store subassets and do the deleting outside the foreach loop
            Dictionary<string, InventoryBase> ResourcesToBeRemoved = new Dictionary<string, InventoryBase>();
            
            if (resource is InventoryFolder)
            {
                InventoryFolder folder = (InventoryFolder)resource;
                foreach (InventoryBase subResource in folder.Children.Values)
                {
                    string suburi = uri;
                    if (!suburi.EndsWith("/"))
                        suburi += "/";
                    suburi += HttpUtility.UrlPathEncode(subResource.Name);
                    ResourcesToBeRemoved.Add(suburi, subResource);
                }

                foreach (KeyValuePair<string, InventoryBase> kvp in ResourcesToBeRemoved)
                    DeleteResource(kvp.Key, kvp.Value, owner, agentID, ref multistatus);
            }

            //TODO: check if resource is locked, if so add key value pair about it to dictionary (resource uri, HttpStatusCode 423 Locked)
            //TODO: check if resource can be deleted by this user, if not add ey value pair about it to dictionary (resource uri, HttpStatusCode 403 Forbidden)

            // Delete the parent resource now that subitems has been removed
            if (!RemoveResource(resource, owner, agentID))
                multistatus.Add(uri, (HttpStatusCode)507);
        }

        protected HttpStatusCode GetHandler(HttpServer.IHttpResponse response, string path, string username)
        {            
            Uri ownerIdentity;
            HttpStatusCode status = HttpStatusCode.NotFound;
            if (Uri.TryCreate(username, UriKind.Absolute, out ownerIdentity))
            {
                UUID agentID = server.FilesystemProvider.IdentityToUUID(ownerIdentity);

                InventoryCollection inventory;
                if (server.FilesystemProvider.TryFetchFilesystem(ownerIdentity, agentID, out inventory) == BackendResponse.Success)
                {
                    InventoryBase invObject = WebDAVHelper.PathToInventory(path, inventory);
                    if (invObject is InventoryItem)
                    {
                        InventoryItem item = (InventoryItem)invObject;

                        byte[] assetData;
                        BackendResponse storageResponse = server.AssetProvider.TryFetchData(item.AssetID, out assetData);

                        if (storageResponse == BackendResponse.Success)
                        {
                            status = HttpStatusCode.OK;
                            response.ContentType = "application/octet-stream";
                            response.AddHeader("Content-Disposition", "attachment; filename=" + item.Name);
                            response.ContentLength = assetData.Length;
                            response.Body.Write(assetData, 0, assetData.Length);
                            Console.WriteLine("returning asset");
                        }
                        else if (storageResponse == BackendResponse.NotFound)
                        {
                            status = HttpStatusCode.NotFound;
                        }
                        else
                        {
                            status = HttpStatusCode.InternalServerError;
                        }

                    }
                    else if (invObject is InventoryFolder)
                    {
                        // "Hardcoded" to html directory listing at the moment
                        InventoryFolder folder = (InventoryFolder)invObject;

                        string body = String.Empty;
                        string myPath = path;

                        if (myPath[myPath.Length - 1] != '/')
                            myPath += "/";
                        if (myPath[0] == '/')
                            myPath = myPath.Substring(1, myPath.Length - 1);

                        // HTML
                        body += "<html>" +
                                "<head>" +
                                "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />" +
                                "<title>" + folder.Name.ToString() + "</title>" +
                                "</head>" +
                                "<body>" +
                                "<p>";

                        // Directory and item listing, directories have / added so they can be identified
                        foreach (InventoryBase item in folder.Children.Values)
                        {
                            body += "<a href=\"" + server.HttpUri.ToString() + myPath + item.Name.ToString() + "\">" + item.Name.ToString();
                            if (item is InventoryFolder)
                                body += "/";
                            body += "</a><br>";
                        }

                        body += "</p></body></html>";

                        UTF8Encoding ecoding = new UTF8Encoding();
                        response.ContentType = "text";
                        response.ContentLength = Encoding.UTF8.GetBytes(body).Length;
                        response.Body.Write(Encoding.UTF8.GetBytes(body), 0, Encoding.UTF8.GetBytes(body).Length);
                        status = HttpStatusCode.OK;
                        Console.WriteLine("[WebDAVFrontend] Returning folder listing");
                    }
                    else //this should not happend
                    {
                        status = HttpStatusCode.NotFound;
                    }
                }
            }
            return status;
        }

        protected HttpStatusCode PutHandler(IHttpRequest request, string path, string username)
        {
            HttpStatusCode status = HttpStatusCode.InternalServerError;
            if (request.Body.Length == 0)
                return status;

            byte[] assetData = request.GetBody();
            Uri ownerIdentity;

            if (Uri.TryCreate(username, UriKind.Absolute, out ownerIdentity))
            {
                UUID agentID = server.FilesystemProvider.IdentityToUUID(ownerIdentity);
                InventoryCollection inventory;
                if (server.FilesystemProvider.TryFetchFilesystem(ownerIdentity, agentID, out inventory) == BackendResponse.Success)
                {
                    string[] pathParts = path.Split('/');
                    string assetName = pathParts[pathParts.Length - 1];
                    path = path.Substring(0, (path.Length - assetName.Length));
                    string searchPath = PathToSearchPath(path);

                    InventoryBase invObject = WebDAVHelper.PathToInventory(searchPath, inventory);

                    if (invObject != null)
                    {
                        if (invObject is InventoryFolder)
                        {
                            InventoryFolder parentFolder = (InventoryFolder)invObject;

                            // How to do this properly, does it need to be done in webdav?
                            //if (request.Headers["Content-type"] == "image/jp2")
                                //MetadataJPEG2000 assetMetadata = new MetadataJPEG2000();
                                //assetMetadata.Components = ; // Do this correctly, how?
                                //assetMetadata.LayerEnds = ; // Do this correctly, how?
                                //assetMetadata.ContentType = request.Headers["Content-type"];
                                //assetMetadata.CreationDate = DateTime.Now;
                                //assetMetadata.Description = assetName;
                                //assetMetadata.ID = UUID.Random();
                                //assetMetadata.Methods = new Dictionary<string, Uri>();
                                //assetMetadata.Name = assetName;
                                //assetMetadata.SHA256 = OpenMetaverse.Utils.SHA256(assetData);
                                //assetMetadata.Temporary = false;
                                //metaData = assetMetadata;

                            MetadataDefault assetMetadata = new MetadataDefault();
                            assetMetadata.ContentType = request.Headers["Content-type"];
                            assetMetadata.CreationDate = DateTime.Now;
                            assetMetadata.Description = assetName;
                            assetMetadata.ID = UUID.Random();
                            assetMetadata.Methods = new Dictionary<string, Uri>();
                            assetMetadata.Name = assetName;
                            assetMetadata.SHA256 = OpenMetaverse.Utils.SHA256(assetData);
                            assetMetadata.Temporary = false;

                            if (server.AssetProvider.TryCreateAsset(assetMetadata, assetData) == BackendResponse.Success)
                            {
                                InventoryItem inventoryItem = new InventoryItem();
                                inventoryItem.AssetID = assetMetadata.ID;
                                inventoryItem.AssetType = parentFolder.Type;
                                inventoryItem.CreationDate = 0;
                                inventoryItem.Creator = agentID;
                                inventoryItem.Owner = agentID;
                                inventoryItem.CurrentPermissions = 2147483647;
                                inventoryItem.NextPermissions = 2147483647;
                                inventoryItem.BasePermissions = 2147483647;
                                inventoryItem.EveryOnePermissions = 2147483647;
                                inventoryItem.GroupPermissions = 2147483647;
                                inventoryItem.InvType = (int)CableBeachMessages.InventoryType.Object;
                                inventoryItem.GroupOwned = false;
                                inventoryItem.Description = assetMetadata.Description;
                                inventoryItem.ID = UUID.Random();
                                inventoryItem.Name = assetMetadata.Name;
                                inventoryItem.ParentID = parentFolder.ID;
                                inventoryItem.SalePrice = 0;
                                
                                if (server.FilesystemProvider.TryCreateItem(ownerIdentity, agentID, inventoryItem) == BackendResponse.Success)
                                    status = HttpStatusCode.Created;
                            }
                        }
                    }
                }
            }
            return status;          
        }

        /// <summary>
        /// Moves resource spesified in source to location spesified in destination
        /// </summary>
        /// <param name="username">The username. Username is empty if AuthenticationType is None</param>
        /// <param name="source">The URI.</param>
        /// <param name="destination">The destination.</param>
        /// <param name="depth">The depth.</param>
        /// <param name="overwrite">if set to <c>true</c> [overwrite].</param>
        /// <param name="ifHeaders">If headers.</param>
        /// <param name="multiStatusValues">The multi status values.</param>
        /// <returns></returns>
        protected HttpStatusCode MoveHandler(string username, Uri source, string destination, DepthHeader depth, bool overwrite, string[] ifHeaders, out Dictionary<String, HttpStatusCode> multiStatusValues)
        {
            multiStatusValues = null;

            //first thing to do is to check source and destination
            string[] srcParts = source.ToString().Split('/');
            string[] dstParts = destination.Split('/');

            //Decode uris
            srcParts = DecodeUri(srcParts);
            dstParts = DecodeUri(dstParts);

            return MoveWorker(username, srcParts, dstParts, depth, overwrite, ifHeaders, out multiStatusValues);
        }

        protected HttpStatusCode MoveWorker(string username, string[]srcParts, string[]dstParts, DepthHeader depth, bool overwrite, string[]ifHeaders, out Dictionary<String, HttpStatusCode> multiStatusValues)
        {
            multiStatusValues = null;

            //now look up the source and destination from the inventory; 3rd field is the first resource element in array
            Uri owner;
            if (Uri.TryCreate(username, UriKind.Absolute, out owner))
            {
                UUID agentID = server.FilesystemProvider.IdentityToUUID(owner);

                InventoryFolder inventory;
                if (server.FilesystemProvider.TryFetchRootFolder(owner, agentID, out inventory) == BackendResponse.Success)
                {
                    //get source parent and
                    //check that source resource exists
                    InventoryBase foundSource = null;
                    InventoryFolder srcParent = null;
                    if (srcParts.Length == 3+rootNumInPath)
                        return HttpStatusCode.Forbidden;
                    if (srcParts.Length == 4 + rootNumInPath)
                    {
                        foreach (InventoryBase item in inventory.Children.Values)
                        {
                            if (item.Name == srcParts[3 + rootNumInPath])
                            {
                                foundSource = item;
                                srcParent = inventory;
                                break;
                            }
                        }
                    }
                    if (srcParts.Length > 4 + rootNumInPath)
                    {
                        string[] srcFolders = new string[srcParts.Length - 4 - rootNumInPath];
                        Array.Copy(srcParts, 3 + rootNumInPath, srcFolders, 0, srcFolders.Length);
                        if (!FindInventoryItem(inventory.Children, srcFolders, out foundSource))
                        {
                            return HttpStatusCode.Conflict;
                        }
                        else
                        {
                            if (foundSource is InventoryFolder)
                            {
                                srcParent = (InventoryFolder)foundSource;
                                foundSource = null;
                                foreach(InventoryBase item in srcParent.Children.Values)
                                {
                                    if (item.Name == srcParts[srcParts.Length - 1])
                                    {
                                        foundSource = item;
                                        break;
                                    }
                                }
                            }
                            else return HttpStatusCode.Conflict;
                        }
                    }

                    if (foundSource == null)
                    {
                        return HttpStatusCode.NotFound; //source not found
                    }

                    //TODO: check that if any of the source resources are locked
                    //if so, check if request has the appropriate lock token,
                    //if not return multistatus createdNew with error

                    //get destination parent and
                    //check if the destination resource exists
                    InventoryBase foundDst = null;
                    InventoryFolder dstParent = null;
                    if (dstParts.Length == 3 + rootNumInPath)
                        return HttpStatusCode.Forbidden;
                    if (dstParts.Length == 4 + rootNumInPath)
                    {
                        dstParent = inventory;
                        foreach (InventoryBase item in inventory.Children.Values)
                        {
                            if (item.Name == dstParts[3 + rootNumInPath])
                            {
                                foundDst = item;
                                break;
                            }
                        }
                    }
                    if (dstParts.Length > 4 + rootNumInPath)
                    {
                        string[] dstFolders = new string[dstParts.Length - 4 - rootNumInPath];
                        Array.Copy(dstParts, 3 + rootNumInPath, dstFolders, 0, dstFolders.Length);
                        InventoryBase dstParentTmp;
                        if (!FindInventoryItem(inventory.Children, dstFolders, out dstParentTmp))
                        {
                            //destination parent not found
                            return HttpStatusCode.Conflict;
                        }
                        else
                        {
                            if (dstParentTmp is InventoryFolder)
                            {
                                dstParent = (InventoryFolder)dstParentTmp;
                                foreach (InventoryBase item in dstParent.Children.Values)
                                {
                                    if (item.Name == dstParts[dstParts.Length-1])
                                    {
                                        foundDst = item;
                                        break;
                                    }
                                }
                            }
                            else
                                return HttpStatusCode.Conflict;
                        }
                    }

                    //create new copy of the object
                    InventoryBase newResource = null;
                    if (foundSource is InventoryItem)
                    {
                        InventoryItem item = (InventoryItem)foundSource;
                        InventoryItem newItem = new InventoryItem(item);
                        newItem.ParentID = dstParent.ID;
                        newItem.ID = UUID.Random();
                        newItem.Name = dstParts[dstParts.Length - 1];
                        newResource = (InventoryBase)newItem;
                    }
                    else if (foundSource is InventoryFolder)
                    {
                        InventoryFolder folder = (InventoryFolder)foundSource;
                        InventoryFolder newFolder = new InventoryFolder(folder);
                        newFolder.ID = UUID.Random();
                        newFolder.Name = dstParts[dstParts.Length - 1];
                        newFolder.ParentID = dstParent.ID;
                        foreach (InventoryBase item in newFolder.Children.Values)
                        {
                            item.ParentID = newFolder.ID;
                        }
                        newResource = (InventoryBase)newFolder;
                    }
                    else return HttpStatusCode.InternalServerError; //unknown type

                    //move the object
                    if (foundDst == null)
                    {
                        //remove source
                        if (!RemoveResource(foundSource, owner, agentID))
                            return HttpStatusCode.PreconditionFailed;

                        //add destination
                        if (SaveInventoryResource(newResource, owner, agentID))
                        {
                            if (newResource is InventoryFolder)
                            {
                                InventoryFolder newFolder = (InventoryFolder)newResource;
                                lock (newFolder.Children)
                                {
                                    foreach (InventoryBase res in newFolder.Children.Values)
                                    {
                                        if (!SaveInventoryResource(res, owner, agentID))
                                            return HttpStatusCode.PreconditionFailed;
                                    }
                                }
                            }
                            return HttpStatusCode.Created;
                        }
                    }
                    else if (foundDst != null && overwrite == true)
                    {
                        if (foundDst is InventoryFolder && newResource is InventoryFolder) //instead of removing old, add content from new to this folder
                        {
                            InventoryFolder newFolder = (InventoryFolder)newResource;
                            InventoryFolder oldFolder = (InventoryFolder)foundDst;
                            foreach (InventoryBase item in newFolder.Children.Values)
                            {
                                item.ParentID = oldFolder.ID;
                                if (!SaveInventoryResource(item, owner, agentID))
                                {
                                    //TODO: Add multistatus values
                                }
                            }

                            //remove source
                            if (!RemoveResource(foundSource, owner, agentID))
                                return HttpStatusCode.PreconditionFailed;

                            if (multiStatusValues != null)
                                return (HttpStatusCode)207;
                            else
                                return HttpStatusCode.NoContent; //all ok
                        }
                        else
                        {
                            //remove source
                            if (!RemoveResource(foundSource, owner, agentID))
                                return HttpStatusCode.PreconditionFailed;

                            //remove old destination
                            if (!RemoveResource(foundDst, owner, agentID))
                                return HttpStatusCode.PreconditionFailed;

                            //add new destination
                            if(SaveInventoryResource(newResource, owner, agentID))
                                return HttpStatusCode.Created;
                        }
                    }
                    else
                    {
                        //destination resource exists and it can't be overwritten
                        return HttpStatusCode.PreconditionFailed;
                    }
                }
            }

            return HttpStatusCode.InternalServerError;
        }

        protected bool SaveInventoryResource(InventoryBase resource, Uri owner, UUID agentID)
        {
            bool ret = true;
            if (resource is InventoryFolder)
            {
                InventoryFolder folder = (InventoryFolder)resource;
                if (BackendResponse.Success == server.FilesystemProvider.TryCreateFolder(owner, agentID, folder))
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else if (resource is InventoryItem)
            {
                InventoryItem item = (InventoryItem)resource;
                if (BackendResponse.Success != server.FilesystemProvider.TryCreateItem(owner, agentID, item))
                    return false;
            }
            else
                ret = false;
            return ret;
        }

        /// <summary>
        /// Removes the resource from FileSystemProvider
        /// </summary>
        /// <param name="resource">The resource.</param>
        /// <param name="owner">The owner.</param>
        /// <param name="agentID">The agent ID.</param>
        /// <returns>True if success, false if failed</returns>
        protected bool RemoveResource(InventoryBase resource, Uri owner, UUID agentID)
        {
            if (resource is InventoryFolder)
            {
                if (BackendResponse.Success != server.FilesystemProvider.TryDeleteFolder(owner, agentID, resource.ID))
                    return false;
            }
            else if (resource is InventoryItem)
            {
                if (BackendResponse.Success != server.FilesystemProvider.TryDeleteItem(owner, agentID, resource.ID))
                    return false;
            }
            else
                return false;
            return true;
        }

        /// <summary>
        /// Decodes the URI.
        /// </summary>
        /// <param name="uriParts">The URI split to resource elements.</param>
        /// <returns>Decoded uri elements</returns>
        protected string[] DecodeUri(string[] uriParts)
        {
            for (int i = 0; i < uriParts.Length; i++)
            {
                uriParts[i] = HttpUtility.UrlDecode(uriParts[i]);
            }
            return uriParts;
        }

        protected HttpStatusCode NewColHandler(string path, string username)
        {
            //User authentication is already done. Only things to check from this point are:
            // * Is this user authorized to create collection to this point (if not return 403)
            // * Does this collection creaton override existing one (if so return 405)
            // * Does parent collection exist (if not return 409)
            // * Is parent a collection (if not return 409)

            if (path[path.Length-1] == '/')
                path = path.Substring(0, path.Length - 1);
            string[] parts = path.Split('/');
            parts = DecodeUri(parts);

            Uri owner;
            if (Uri.TryCreate(username, UriKind.Absolute, out owner))
            {
                InventoryFolder rootFolder; 
                UUID agentID = server.FilesystemProvider.IdentityToUUID(owner);

                if (server.FilesystemProvider.TryFetchRootFolder(owner, agentID, out rootFolder) == BackendResponse.Success)
                {
                    if (parts.Length == 1+rootNumInPath) // Trying to create root
                    {
                        return HttpStatusCode.MethodNotAllowed;
                    }
                    else if (parts.Length == 2+rootNumInPath) // Trying to create folder under root
                    {
                        foreach (InventoryBase folder in rootFolder.Children.Values)
                        {
                            if (folder.Name == parts[1 + rootNumInPath])
                                return HttpStatusCode.MethodNotAllowed; // Folder already exists
                        }
                        if (server.FilesystemProvider.TryCreateFolder(owner, agentID, new InventoryFolder(parts[1 + rootNumInPath], agentID, rootFolder.ID, 0)) == BackendResponse.Success)
                        {
                            return HttpStatusCode.Created;
                        }
                    }
                    else if (parts.Length > 2 + rootNumInPath) // Creating subfolders
                    {
                        InventoryBase currentItem = null;
                        string folderName = parts[parts.Length - 1];
                        string[] pathFolders = new string[parts.Length - 2 - rootNumInPath];
                        Array.Copy(parts, 1 + rootNumInPath, pathFolders, 0, pathFolders.Length); //copy all but the first and the last to pathFolders

                        if (FindInventoryItem(rootFolder.Children, pathFolders, out currentItem))
                        {
                            if (currentItem is InventoryFolder)
                            {
                                return SaveNewFolder((InventoryFolder)currentItem, parts[parts.Length - 1], owner, agentID);
                            }
                        }
                    }
                }
            }
            return HttpStatusCode.Conflict;  
        }

        protected HttpStatusCode SaveNewFolder(InventoryFolder rootFolder, string newFolderName, Uri owner, UUID agentId)
        {
            //check that the item with same name doesn't exist in current folder
            InventoryBase foundItem;
            if (FindInventoryItem(rootFolder.Children, new string[1] { newFolderName }, out foundItem))
            {
                return HttpStatusCode.MethodNotAllowed;
            }

            //no errors
            BackendResponse response = server.FilesystemProvider.TryCreateFolder(owner, agentId,
                new InventoryFolder(newFolderName, agentId, rootFolder.ID, 0));
            if (response == BackendResponse.Success)
            {
                return HttpStatusCode.Created;
            }
            return HttpStatusCode.Conflict;
        }

        #region Helper methods to find items in inventory

        /// <summary>
        /// Finds the sub folder in inventory.
        /// </summary>
        /// <param name="folders">The folders in inventory</param>
        /// <param name="folderName">Name of the folder.</param>
        /// <param name="foundFolder">The found folder.</param>
        /// <returns>True if found, false if not found</returns>
        protected bool FindSubFolderInInventory(Dictionary<UUID, InventoryFolder> folders, string folderName, out InventoryFolder foundFolder)
        {
            foreach (InventoryFolder folder in folders.Values)
            {
                if (folder.Name == folderName)
                {
                    foundFolder = folder;
                    return true;
                }
            }
            foundFolder = null;
            return false;
        }

        /// <summary>
        /// Finds the inventory item in valuecollection hierarchy
        /// </summary>
        /// <param name="items">The items.</param>
        /// <param name="path">The path split in to string array</param>
        /// <param name="foundItem">The found item or the last item found when searching</param>
        /// <returns>true if item is found, false if not</returns>
        protected bool FindInventoryItem(Dictionary<UUID, InventoryBase> items, string[] path, out InventoryBase foundItem)
        {
            foundItem = null;
            for (int i = 0; i < path.Length; i++)
            {
                bool itemFound = false;
                foreach (InventoryBase item in items.Values)
                {
                    if (item.Name == path[i])
                    {
                        foundItem = item;
                        itemFound = true;
                        break;
                    }
                }
                if (!itemFound)
                {
                    return false; //no item found in this folder
                }
                else
                {
                    if(i<path.Length-1)
                    {
                        if (foundItem is InventoryFolder)
                        {
                            items = ((InventoryFolder)foundItem).Children;
                        }
                        else
                        {
                            return false; //found item is not folder and therefore can't contain other folders
                        }
                    }
                }
            }
            return true;
        }

        protected string PathToSearchPath(string path)
        {
            string searchPath = String.Empty;
            string[] pathParts = path.Split('/');
            for (int i = rootNumInPath; i < pathParts.Length; i++)
            {
                searchPath += pathParts[i];
                if (i != pathParts.Length - 1)
                    searchPath += "/";
            }
            return searchPath;
        }

        #endregion

        public virtual void Stop()
        {
            webdav.OnPropFind -= PropFindHandler;
            webdav.OnLock -= LockHandler;
            webdav.OnUnlock -= UnlockHandler;
            webdav.OnNewCol -= NewColHandler;
            webdav.OnMove -= MoveHandler;
            webdav.OnGet -= GetHandler;
            webdav.OnPut -= PutHandler;
            webdav.OnDelete -= DeleteHandler;
            webdav.OnCopy -= CopyHandler;
            webdav.OnPropPatch -= PropPatchHandler;
            webdav.OnDigestAuthenticate -= OnDigestAuthenticateHandler;
            webdav = null;
        }

        IList<IWebDAVResource> PropFindHandler(string username, string path, DepthHeader depth)
        {
            Uri owner;
            if (Uri.TryCreate(username, UriKind.Absolute, out owner))
            {
                UUID agentID = server.FilesystemProvider.IdentityToUUID(owner);

                // FIXME: We should never be retrieving the entire filesystem. This puts the entire inventory for a user into memory
                InventoryCollection inventory;
                if (server.FilesystemProvider.TryFetchFilesystem(owner, agentID, out inventory) == BackendResponse.Success)
                {
                    List<IWebDAVResource> davEntries = new List<IWebDAVResource>();
                    string searchPath = HttpUtility.UrlDecode(PathToSearchPath(path)); //convert %20 in to space etc.

                    InventoryBase invObject = WebDAVHelper.PathToInventory(searchPath, inventory);
                    if (invObject == null) //Couldn't find the path requested. Returning empty list.
                    {
                        return new List<IWebDAVResource>(0);
                    }
                    //instead of just trying to convert inventory item to WebDAV resource, first try to fetch it from WebDAVPropertyProvider
                    IWebDAVResource resource = server.WebDAVPropertyProvider.Load(path);
                    if (resource == null) //didn't find, now try converting from inventory object
                    {
                        resource = WebDAVHelper.InventoryToDAV(path, invObject);
                        server.WebDAVPropertyProvider.Save(resource);
                    }

                    //Only add the root to response if the client wants it
                    if (depth != DepthHeader.InfinityNoRoot && depth != DepthHeader.OneNoRoot)
                    {
                        resource.Path = HttpUtility.UrlPathEncode(resource.Path);
                        davEntries.Add(resource);
                    }

                    if (invObject is InventoryFolder && (depth == DepthHeader.One || depth == DepthHeader.Infinity))
                    {
                        InventoryFolder folder = (InventoryFolder)invObject;
                        string currentPath = path;
                        if (!currentPath.EndsWith("/"))
                            currentPath += "/";
                        foreach (InventoryBase child in folder.Children.Values)
                        {
                            string name = child.Name;

                            name = HttpUtility.UrlPathEncode(name); //convert spaces to %20 etc.
                            resource = server.WebDAVPropertyProvider.Load(currentPath + name); //first try WebDAVPropertyProvider
                            if (resource == null)
                            {
                                resource = WebDAVHelper.InventoryToDAV(currentPath + name, child);
                                server.WebDAVPropertyProvider.Save(resource);
                            }
                            if (resource != null)
                                davEntries.Add(resource);
                        }
                    }

                    return davEntries;
                }
            }

            return new List<IWebDAVResource>(0);
        }

        protected WebDAVLockResponse LockHandler(WebDAVLockRequest request)
        {
            WebDAVLockResponse response = new WebDAVLockResponse();
            if (!lockedResources.ContainsKey(request.Path))
            {
                //create locktoken
                //more information how to really generate the token, see: rfc2518 section 6.4
                string locktoken = "opaquelocktoken:" + UUID.Random().ToString();
                response.LockToken = locktoken;
                response.LockScope = request.LockScope;
                response.LockType = request.LockType;
                response.Depth = "0";
                response.Timeout = "Infinite";
                response.OwnerNamespaceUri = request.OwnerNamespaceUri;
                response.OwnerValue = request.OwnerValue;
                response.OwnerValues = request.OwnerValues;
                response.HttpStatusCode = HttpStatusCode.OK;
                lockedResources.Add(request.Path, response);
            }
            else
            {
                if (lockedResources[request.Path].LockScope == LockScope.shared && request.LockScope == LockScope.shared)
                {
                    //quite special case. no implementation yet
                    ;
                }
                response.HttpStatusCode = (HttpStatusCode)423; //locked
            }
            return response;
        }

        protected HttpStatusCode UnlockHandler(string path, string locktoken, string username)
        {
            if (lockedResources.ContainsKey(path))
            {
                WebDAVLockResponse response = lockedResources[path];
                if (response.LockToken == locktoken)
                {
                    if (true) //username check should be here
                    {
                        lockedResources.Remove(path);
                        return HttpStatusCode.NoContent;
                    }
                    else
                    {
                        return HttpStatusCode.Unauthorized;
                    }
                }
                else
                {
                    return HttpStatusCode.PreconditionFailed;
                }
            }
            else
            {
                return HttpStatusCode.PreconditionFailed;
            }
        }
    }
}
